﻿#include "scope_curl.hh"
#include <cstring>

kratos::service::ScopeCurl::ScopeCurl() { curl_global_init(CURL_GLOBAL_ALL); }

kratos::service::ScopeCurl::~ScopeCurl() { curl_global_cleanup(); }

// 获取CURL实例

CURL *kratos::service::ScopeCurl::get() const { return curl_; }

// 获取返回的数据指针

const char *kratos::service::ScopeCurl::get_content() const {
  return buffer_.get();
}

// 获取返回的数据长度

size_t kratos::service::ScopeCurl::get_content_size() const {
  return buffer_len_;
}

// 判空
// 将获取的数据写入std::string

auto kratos::service::ScopeCurl::to_string(std::string &s) -> bool {
  s.assign(buffer_.get(), buffer_len_);
  return true;
}

// 将返回的数据写入std::ostream

auto kratos::service::ScopeCurl::to_stream(std::ostream &os) -> bool {
  os.write(buffer_.get(), buffer_len_);
  os.flush();
  return true;
}

auto kratos::service::ScopeCurl::to_stream(std::FILE *fp) -> bool {
  fwrite(buffer_.get(), 1, buffer_len_, fp);
  return true;
}

// 进行一次CURL调用
// @param url 远程URL

auto kratos::service::ScopeCurl::perform(const std::string &url) -> bool {
  if (!curl_) {
    curl_ = curl_easy_init();
  }
  if (!curl_) {
    return false;
  }
  init_curl(url);
  bool retval = true;
  constexpr static int max_retry_count = 3;
  int current_retry_count = 0;
  while (true) {
    auto curl_ret = curl_easy_perform(curl_);
    if ((curl_ret != CURLE_OK) && (curl_ret == CURLE_OPERATION_TIMEDOUT)) {
      // 支持本次下载失败续传
      current_retry_count += 1;
      if (current_retry_count >= max_retry_count) {
        retval = false;
        break;
      }
      auto range = buffer_len_;
      auto range_str = std::to_string(range) + "-";
      curl_easy_reset(curl_);
      init_curl(url);
      curl_easy_setopt(curl_, CURLOPT_RANGE, range_str.c_str());
    } else if (curl_ret == CURLE_OK) {
      break;
    } else {
      retval = false;
      break;
    }
  }
  curl_easy_cleanup(curl_);
  curl_ = nullptr;
  return retval;
}

// 设置CURL参数

auto kratos::service::ScopeCurl::init_curl(const std::string &url) -> void {
  curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT, 10L);
  curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
  curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1L);
  curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 1L);
  curl_easy_setopt(curl_, CURLOPT_HEADER, 0L);
  curl_easy_setopt(curl_, CURLOPT_MAXREDIRS, 5);
  curl_easy_setopt(curl_, CURLOPT_FOLLOWLOCATION, 1);
  curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
  curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, &ScopeCurl::curl_write_fn);
  curl_easy_setopt(curl_, CURLOPT_WRITEDATA, reinterpret_cast<void *>(this));
}

// 写入数据到缓冲区
// @param data 数据指针
// @param size 数据长度

auto kratos::service::ScopeCurl::write(const char *data, size_t size) -> void {
  bool expanded = false; // 是否扩充缓冲区
  while (max_buffer_len_ < buffer_len_ + size) {
    // 新的最大长度
    max_buffer_len_ += max_buffer_len_;
    expanded = true;
  }
  if (expanded) {
    // 建立新的缓冲区并拷贝旧数据进入
    auto new_buffer = kratos::make_unique_pool_ptr<char>(max_buffer_len_);
    if (buffer_len_) {
      std::memcpy(new_buffer.get(), buffer_.get(), buffer_len_);
    }
    buffer_.swap(new_buffer);
  }
  // 写入数据
  std::memcpy(buffer_.get() + buffer_len_, data, size);
  buffer_len_ += size;
}

// CURL写入数据回调

size_t kratos::service::ScopeCurl::curl_write_fn(void *contents, size_t size,
                                                 size_t nmemb, void *userp) {
  auto *curl = reinterpret_cast<ScopeCurl *>(userp);
  size_t real_size = size * nmemb;
  curl->write(reinterpret_cast<const char *>(contents), real_size);
  return real_size;
}
